package com.winit.project.util;

import com.alibaba.fastjson.JSONObject;
import com.winit.common.util.*;
import com.winit.core.server.base.ExecResultHandler;
import com.winit.core.server.linux.LinuxLocalServer;
import com.winit.project.core.bean.PackageTypeEnum;
import com.winit.project.core.bean.ProjectInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by wangjian on 16/6/3.
 */
public class ProjectUtil {

    public static final String PROJECT_DIR = ConfigUtil.getValue("rootPath") + "Beetle/projects/";

    public static final String PROJECT_FILE_DIR = ConfigUtil.getValue("project_file_dir");

    private static final Logger logger = LoggerFactory.getLogger(ProjectUtil.class);

    public static final String BUILD_DIR = "default";

    /**
     * 根据工程名获取主干工程名
     *
     * @param projectName
     * @return
     */
    public static String getTrunkProjectName(String projectName) {
        if (projectName.indexOf("_") != -1) {
            return projectName.split("_")[0];
        } else {
            return projectName;
        }
    }

    /**
     * 根据工程名获取主干层目录路径
     *
     * @param projectName
     * @return
     */
    public static String getTrunkFilePath(String projectName) {
        StringBuffer path = new StringBuffer();
        path.append(PROJECT_DIR).append(getTrunkProjectName(projectName)).append("/");
        return path.toString();
    }

    /**
     * 获取工程目录路径
     *
     * @param projectName
     * @return
     */
    public static String getProjectFilePath(String projectName) {
        StringBuffer path = new StringBuffer();
        path.append(getTrunkFilePath(projectName)).append(projectName).append("/");
        return path.toString();
    }

    /**
     * 获取工程版本目录路径
     *
     * @param projectName
     * @param editionNum
     * @return
     */
    public static String getEditionPath(String projectName, String editionNum) {
        StringBuffer path = new StringBuffer();
        path.append(getProjectFilePath(projectName)).append(editionNum).append("/");
        return path.toString();
    }

    /**
     * 获取工程代码保存路径
     *
     * @param projectName
     * @return
     */
    public static String getProjectCodePath(String projectName) {
        StringBuffer path = new StringBuffer();
        path.append(getProjectFilePath(projectName)).append("projectCode/");
        return path.toString();
    }

    public static JSONObject loadFromFile(String filePath) throws Exception {
        logger.info("加载工程信息,path=" + filePath);
        File file = new File(filePath);
        if (!file.exists()) {
            logger.error("加载工程信息,文件不存在,path=" + filePath);
            throw new Exception("加载工程信息,文件不存在,path=" + filePath);
        }
        JSONObject object = JSONFileUtil.getFileJsonForBean(filePath, JSONObject.class);
        logger.info("加载工程信息,object=" + object);
        return object;
    }

    public static boolean saveToFile(String filePath, JSONObject object) throws Exception {
        logger.info("保存工程信息,path=" + filePath);
        File file = new File(filePath);
        if (!file.getParentFile().exists()) {
            logger.error("保存工程信息,目录不存在.path=" + file.getParentFile().getAbsolutePath());
            throw new Exception("保存工程信息,目录不存在.path=" + file.getParentFile().getAbsolutePath());
        }
        boolean flag = JSONFileUtil.saveFileJson(filePath, object);
        logger.info("保存工程信息,object=" + object + ",flag=" + flag);
        return flag;
    }

    /**
     * 创建版本目录
     *
     * @return
     */
    public static boolean createEditionDir(String projectName, String editionNum) throws Exception {
        LinuxLocalServer localServer = LinuxLocalServer.getInstance();
        StringBuffer cmd = new StringBuffer();
        cmd.append("source /etc/profile && cd ").append(getProjectFilePath(projectName)).append(" && mkdir ").append(editionNum)
                .append(" && cd ").append(editionNum).append(" && mkdir logs temp target && echo {} > info.json && touch logs/build.log logs/release.log");
        ExecResultHandler handler = localServer.doExecRtnHandler(cmd.toString());
        logger.info("创建版本目录,projectName=" + projectName + ",editionNum=" + editionNum);
        if (!handler.isSuccess()) {
            logger.error("创建版本目录失败,projectName=" + projectName + ",editionNum=" + editionNum + ",msg=" + handler.getMessage());
            throw new Exception("创建版本目录失败,projectName=" + projectName + ",editionNum=" + editionNum + ",msg=" + handler.getMessage());
        }
        return true;
    }

    /**
     * 检出工程配置文件
     *
     * @param configSvnUrl 配置文件对应的svn路径
     * @param toPath       配置文件要保存的路径 例:配置文件要下载到版本release/DEV01_vm.gzidc下 应传{版本路径}/release
     * @param envName      检出配置文件后保存的文件夹名 例:DEV01_vm.gzidc
     * @return
     */
    public static boolean checkOutConfigFile(String configSvnUrl, String toPath, String envName) throws Exception {
        File file = new File(toPath);
        if (!file.exists()) {
            file.mkdir();
        }
        LinuxLocalServer localServer = LinuxLocalServer.getInstance();
        StringBuffer cmd = new StringBuffer();
        cmd.append("source /etc/profile && cd ").append(toPath).append(" && rm -rf ").append(envName).append(" && svn export --force --username ")
                .append(Constants.SVN_USER_NAME).append(" ").append(configSvnUrl).append("@HEAD ").append(envName).append(" && rm -rf ")
                .append(envName).append(".tgz && tar -zcf ").append(envName).append(".tgz ").append(envName);
        ExecResultHandler handler = localServer.doExecRtnHandler(cmd.toString());
        logger.info("com.winit.Project.util.ProjectUtil.checkOutConfigFile,toPath=" + toPath
                + ",configSvnUrl=" + configSvnUrl + ",envName=" + envName + ",返回:" + handler.getMessage());
        if (!handler.isSuccess()) {
            throw new Exception("检出工程配置文件失败,e:" + handler.getMessage());
        }
        return true;
    }

    /**
     * 校验环境配置文件是否正确
     *
     * @param configPath     环境配置文件保存路径 例:{editionPath}/release/QA01_vm.gzidc
     * @param baseConfigPath 工程配置文件路径 例:{projectPath}/projectCode/{subModule}
     * @return
     */
    public static boolean checkConfigFile(String configPath, String baseConfigPath) throws Exception {
        List<String> fileList = new ArrayList<>();
        fileList = FileUtil.getFileList(new File(configPath), fileList);
        Integer size = fileList.size();
        if (size > 0) {
            for (int i = 0; i < size; i++) {
                String comFile = fileList.get(i);
                String baseFile = comFile.replace(configPath, baseConfigPath);
                Map<String, String> rtnM = null;
                if (comFile.endsWith(".php")) {
                    rtnM = FileUtil.fileCompareByDiff(configPath, baseFile, comFile);
                } else if(comFile.endsWith(".properties")){
                    rtnM = FileUtil.fileCompare(baseFile, comFile);
                }else  if(comFile.endsWith(".js")){
                    rtnM = FileUtil.fileCompare(baseFile, comFile,"js");
                }else {
                    continue;
                }
                if ("1".equals(rtnM.get("code"))) {
                    String[] args = comFile.split("/");
                    String fileName = args[args.length - 1];
                    logger.error("校验失败,环境配置文件错误,fileName=" + fileName + ",错误描述:" + rtnM.get("desc"));
                    throw new Exception("环境配置文件" + fileName + "与工程svn目录下配置文件不一致.描述:" + rtnM.get("desc"));
                }
            }

        } else {
            logger.error("目录" + configPath + "下没配置文件!");
            throw new Exception("目录" + configPath + "下没配置文件!");
        }
        return true;
    }

    /**
     * 发布时,在本地替换配置文件
     *
     * @param typeEnum          工程类型
     * @param configPath        要替换的配置文件路径 {editionPath}/release/QA01_vm.gzidc
     * @param savePath          替换配置文件后新包的存放路径(文件夹)
     * @param buildPackagePath  构建成功后基础包的存放路径(文件)
     * @param buildPackageName  构建后包名称
     * @param projectConfigPath 工程内配置文件相对路径
     */
    public static void replaceConfigFile(PackageTypeEnum typeEnum, String configPath, String savePath
            , String buildPackagePath, String buildPackageName, String projectConfigPath, boolean isVmEnv) throws Exception {
        File configFile = new File(configPath);
        if (!configFile.exists()) {
            logger.error("com.winit.Project.util.ProjectUtil.replaceConfigFile 目录不存在。" + configPath);
            throw new Exception("替换配置文件,配置文件存放目录不存在。configPath=" + configPath);
        }
        File saveFile = new File(savePath);
        if (!saveFile.exists()) {
            saveFile.mkdirs();
        }
        File warFile = new File(buildPackagePath);
        if (!warFile.exists()) {
            logger.error("com.winit.Project.util.ProjectUtil.replaceConfigFile 目录不存在。" + warFile);
            throw new Exception("替换配置文件,工程包不存在,请确认工程是否构建成功。warFile=" + warFile);
        }

        LinuxLocalServer localServer = LinuxLocalServer.getInstance();
        StringBuffer cmd = new StringBuffer();
        switch (typeEnum) {
            case MAVEN_WAR:
                cmd.append("source /etc/profile && cd ").append(savePath).append(" && rm -rf WEB-INF ").append(buildPackageName).append(".war")
                        .append(" && cp ").append(buildPackagePath).append(" ").append(buildPackageName).append(".war").append(" && mkdir -p WEB-INF/classes/ && cp -Rf ")
                        .append(configPath).append("/").append(projectConfigPath).append("/* WEB-INF/classes/ && zip -m ").append(buildPackageName).append(".war WEB-INF/classes/*");
                break;
            case PHP:
                String[] dir = projectConfigPath.split("/");
                //虚拟机环境发布
                if (isVmEnv) {
                    cmd.append("source /etc/profile && cd ").append(savePath).append(" && cp ").append(buildPackagePath).append(" ").append(buildPackageName).append(".zip && zip -m ")
                            .append(buildPackageName).append(".zip ").append(projectConfigPath).append("/*");
                } else {//非停机发布
                    cmd.append("source /etc/profile && cd ").append(savePath).append(" && rm -rf ").append(dir[0]).append(" ").append(buildPackageName).append(".zip && cp ")
                            .append(buildPackagePath).append(" ").append(buildPackageName).append(".zip").append(" && cp -Rf ").append(configPath).append("/").append(dir[0])
                            .append(" ").append(dir[0]).append(" && zip -m ").append(buildPackageName).append(".zip ").append(projectConfigPath).append("/*");
                }
                break;
            case SPRING_BOOT_JAR:
                cmd.append("source /etc/profile && cd ").append(savePath).append(" && rm -rf * ").append(" && cp ").append(buildPackagePath).append(" ").append(buildPackageName).append(".jar").append(" && cp -Rf ")
                        .append(configPath).append("/").append(projectConfigPath).append("/* ").append(savePath).append(" && tar -zcf ").append(buildPackageName).append(".tgz * ");
                break;
            default:
                break;
        }
        ExecResultHandler handler = localServer.doExecRtnHandler(cmd.toString());
        logger.info("com.winit.Project.util.ProjectUtil.replaceConfigFile.configPath=" + configPath + ",savePath=" + savePath
                + ",buildPackagePath=" + buildPackagePath + ",buildPackageName=" + buildPackageName + ".返回:" + handler.getMessage());
        if (!handler.isSuccess()) {
            throw new Exception("替换配置文件失败,e:" + handler.getMessage());
        }
    }

    public static String getEditionNasNum(String editionPath) {
        String name = "";
        StringBuffer path = new StringBuffer();
        path.append(editionPath).append("/target/");
        File file = new File(path.toString());
        if (file.exists()) {
            for (File f : file.listFiles()) {
                if ("temp".equals(f.getName())
                        || "sql".equals(f.getName())) {
                    continue;
                }
                if (!f.isDirectory()) {
                    continue;
                }
                name = f.getName();
            }
        }
        return name;
    }

    /**
     * 提交测试时将本地构建版本拷贝至nas
     *
     * @return
     */
    public static void scpMvnEdition(PackageTypeEnum typeEnum, String projectName, String editionNum) {
        StringBuffer path = new StringBuffer();
        String editionPath = ProjectUtil.getEditionPath(projectName, editionNum);
        switch (typeEnum) {
            case MAVEN_WAR:
                String nasNum = getEditionNasNum(editionPath);
                if (StringUtils.isEmpty(nasNum)) {
                    logger.info("提交测试时将本地构建版本拷贝至nas,版本号为空.editionPath=" + editionPath);
                    return;
                }
                path.append(editionPath).append("/target/").append(nasNum);
                logger.info("提交测试时将本地构建版本拷贝至nas,path=" + path.toString());
                break;
            case PHP:
                path.append(editionPath).append("/target");
                logger.info("提交测试时将本地构建版本拷贝至nas,path=" + path.toString());
                break;
            case ANT_ANDROID:
                path.append(editionPath).append("/target");
                logger.info("提交测试时将本地构建版本拷贝至nas,path=" + path.toString());
                break;
            case SPRING_BOOT_JAR:
                path.append(editionPath).append("/target");
                logger.info("提交测试时将本地构建版本拷贝至nas,path=" + path.toString());
                break;
            default:
                break;
        }

        String toPath = getNasPath(projectName, editionNum, "build");
        File toFile = new File(toPath);
        if (!toFile.exists()) {
            toFile.mkdirs();
            logger.warn("提交测试时将本地构建版本拷贝至nas,nas目录不存在,创建。toPath=" + toPath);
        }
        if (StringUtils.isEmpty(path.toString())) {
            logger.info("提交测试时将本地构建版本拷贝至nas,path=null");
            return;
        }
        File file = new File(path.toString());
        if (file.exists()) {
            String str = ShellRunUtil.runCmds("cp", "-Rf", path.append("/*").toString(), toPath);
            logger.info("提交测试时将本地构建版本拷贝至nas,path=" + path.toString() + ",return=" + str);
        } else {
            logger.info("提交测试时将本地构建版本拷贝至nas,文件不存在,path=" + path.toString());
        }
    }

    /**
     * 测试通过时,将版本拷贝至release目录
     *
     * @return
     */
    public static void scpMvnEditionToRelease(String projectName, String editionNum) {
        String fromPath = getNasPath(projectName, editionNum, "build");

        logger.info("测试通过,将版本拷贝至release目录,fromPath=" + fromPath);
        String toPath = getNasPath(projectName, editionNum, "Winit_release");
        File toFile = new File(toPath);
        if (!toFile.exists()) {
            toFile.mkdirs();
            logger.warn("测试通过,将版本拷贝至release目录,nas目录不存在,创建。toPath=" + toPath);
        }
        File file = new File(fromPath);
        if (file.exists()) {
            String str = ShellRunUtil.runCmds("cp", "-Rf", fromPath + "*", toPath);
            logger.info("测试通过,将版本拷贝至release目录,path=" + fromPath + ",return=" + str);
        } else {
            logger.info("测试通过,将版本拷贝至release目录,文件不存在,path=" + fromPath);
        }
    }

    public static String getNasPath(String projectName, String editionNum, String type) {
        StringBuffer path = new StringBuffer();
        path.append(ConfigUtil.getValue("mount_dir")).append("/").append(type).append("/beetle/").append(ProjectUtil.getTrunkProjectName(projectName))
                .append("/").append(projectName).append("/").append(editionNum).append("/");
        return path.toString();
    }

    /**
     * 获取工程相关文档保存路径
     *
     * @param projectName 工程名称
     * @param type        文档类型  sql excel
     * @return
     */
    public static String getProjectFileDir(String projectName, String type) {
        StringBuffer path = new StringBuffer();
        path.append(PROJECT_FILE_DIR).append("/").append(getTrunkProjectName(projectName)).append("/")
                .append(projectName).append("/").append(type);
        return path.toString();
    }

    public static String getProjectConfigPath(ProjectInfo projectInfo, String configFile) {
        StringBuffer path = new StringBuffer();
        path.append(getProjectCodePath(projectInfo.getProjectName()));
        String subModule = projectInfo.getSubModule();
        if (!StringUtils.isEmpty(subModule)) {
            path.append(subModule).append("/");
        }
        path.append(projectInfo.getConfigPath()).append("/").append(configFile);
        return path.toString();
    }

}
